/**
* @file geometry.h
* @brief 几何信息
*/
#ifndef _COMMON_GEOMETRY_H_
#define _COMMON_GEOMETRY_H_

#include "base.h"
#include <cmath>
#include "string_common.h"
#include "../../3rd/libjson/json.h"


// CVector3::FromString  格式 [xxxx.xx,yyyy.yy,zzzz.zz]
// CVector3::ToString 格式 xxxx.xx,yyyy.yy,zzzz.zz ,由于客户端解析中括号的问题格式不同。
#define MAX_VEC3_STRING_LENGTH 23  //xxxx.xx,yyyy.yy,zzzz.zz

#ifdef _MSC_VER
#define M_PI 3.14159265358979323846
#endif

namespace common
{

	class CVector3;
	class CCircle;
	class CShape;
	const double DOUBLE_ERROR = 0.000001;
	int CompareFloat(float data1, float data2);
	int CompareDouble(double data1, double data2);
	int GetQuadrant(const CVector3& point);

	/**
	* @brief VerticalVector 求向量的垂直向量
	*
	* @param point 原向量
	*
	* @return 垂直向量
	*/
	CVector3 VerticalVector(const CVector3& ivector);

	/**
	* @brief CalcDirectionVector 求两个点连线的单位向量
	*
	* @param start_point 起点
	* @param end_point 终点
	*
	* @return 单位向量
	*/
	CVector3 CalcDirectionVector(const CVector3& start_point, const CVector3& end_point);

	float CalcCross(const CVector3& point1, const CVector3& point2, const CVector3& point3);

	/**
	* @brief MyDistance 求两点之间的距离,如果只是用来做距离比较,用WithinDistance
	*
	* @param cur
	* @param target
	*
	* @return 距离：厘米
	*/
	float MyDistance(const CVector3& cur, const CVector3& target);

	/**
	* @brief WithinDistance 测试两点距离是否小于参数distance
	*
	* @param cur
	* @param target
	* @param distance 厘米
	*
	* @return
	*/
	bool WithinDistance(const CVector3& cur, const CVector3& target, float distance);

	/**
	* @brief WithinDistance 测试两点距离是否小于参数distance，用相减的方式处理;
	*
	* @param cur
	* @param target
	* @param distance 厘米
	*
	* @return
	*/
	bool WithinDistanceEx(const CVector3& cur, const CVector3& target, float distance);

	// 判断线段和圆是否相交
	bool IsCrossSegmentWithCircle(const CVector3& start_point, const CVector3& end_point, const CCircle& circle);

	// 判断形状和圆是否相交
	bool IsCrossShapeWithCircle(const CShape& shape, const CCircle& circle);


	/**
	* @brief 3D点;
	*/
	class CVector3
	{
	public:
		CVector3()
		{
			x_ = 0;
			y_ = 0;
			z_ = 0;
		}

		CVector3(float x, float y, float z)
		{
			x_ = x;
			y_ = y;
			z_ = z;
		}

		CVector3(const CVector3& v)
		{
			x_ = v.x_;
			y_ = v.y_;
			z_ = v.z_;
		}

		~CVector3() { }

		float x() const { return x_; }
		float y() const { return y_; }
		float z() const { return z_; }

		void set_x(float x) { x_ = x; }
		void set_y(float y) { y_ = y; }
		void set_z(float z) { z_ = z; }

		void Clear()
		{
			x_ = 0.0f;
			y_ = 0.0f;
			z_ = 0.0f;
		}

		bool FromString(const std::string& str)
		{
			if (str.size() < 2)
			{
				return false;
			}
			std::string tmp = str.substr(1, str.size() - 2);
			std::vector<float> result;
			::common::SplitString(tmp, ",", result);
			if (result.size() != 3)
			{
				return false;
			}
			x_ = result[0];
			y_ = result[1];
			z_ = result[2];
			return true;
		}

		std::string ToString() const
		{
			char ch[MAX_VEC3_STRING_LENGTH + 1];
			snprintf(ch, MAX_VEC3_STRING_LENGTH, "%.2f,%.2f,%.2f", x_, y_, z_);
			ch[MAX_VEC3_STRING_LENGTH] = '\0';
			return ch;
		}

		bool ReadFromJson(const Json::Value& position)
		{
			x_ = position["x"].asDouble();
			y_ = position["y"].asDouble();
			z_ = position["z"].asDouble();
			return true;
		}

		bool WriteToPB(Json::Value& position) const
		{
			position["x"] = x_;
			position["y"] = y_;
			position["z"] = z_;
			return true;
		}

		CVector3& operator += (const CVector3& v)
		{
			x_ += v.x();
			y_ += v.y();
			z_ += v.z();
			return *this;
		}

		CVector3& operator -= (const CVector3& v)
		{
			x_ -= v.x();
			y_ -= v.y();
			z_ -= v.z();
			return *this;
		}

		CVector3& operator *= (float f)
		{
			x_ *= f;
			y_ *= f;
			z_ *= f;
			return *this;
		}

		CVector3& operator /= (float f)
		{
			return *this *= (1.0f / f);
		}


		CVector3 operator + (const CVector3& v) const
		{
			CVector3 result(x_ + v.x_, y_ + v.y_, z_ + v.z_);
			return result;
		}

		CVector3 operator - (const CVector3& v) const
		{
			CVector3 result(x_ - v.x_, y_ - v.y_, z_ - v.z_);
			return result;
		}

		CVector3 operator * (float f) const
		{
			CVector3 result(x_ * f, y_ * f, z_ * f);
			return result;
		}

		CVector3 operator / (float f) const
		{
			return *this * (1.0f / f);
		}

		CVector3 operator -() const
		{
			return CVector3(-x_, -y_, -z_);
		}

		bool operator == (const CVector3& v) const
		{
			return  CompareFloat(v.x_, x_) == 0 && CompareFloat(v.z_, z_) == 0;
		}

		bool operator != (const CVector3& v) const
		{
			return !(*this == v);
		}

		float length() const
		{
			return sqrt(x_ * x_ + z_ * z_);
		}

		float sqr_length() const
		{
			return x_ * x_ + z_ * z_;
		}

		CVector3 normalized() const
		{
			if (zero() == true)
			{
				return *this;
			}
			float f = length();
			return *this / f;
		}

		void rotate(float angle)
		{
			angle += ::atan2f(z_, x_);
			float len = length();
			x_ = len * ::cos(angle);
			z_ = len * ::sin(angle);
		}

		//获取角度[0,2π)
		float toangle()
		{
			float f = ::atan2f(z_, x_);
			return z_ < 0 ? f + 2 * M_PI : f;
		}

		//角度转换成矢量
		//f[0,2π)
		void fromangle(float f)
		{
			x_ = ::cos(f);
			z_ = ::sin(f);
		}

		bool zero() const
		{
			return (x_ == 0 && z_ == 0);
		}

	private:
		float x_;
		float y_;
		float z_;
	};

	enum EShapeType
	{
		SHAPE_TYPE_NONE = 0,
		SHAPE_TYPE_RECT = 1,
		SHAPE_TYPE_CIRCLE = 2,
		SHAPE_TYPE_POLYGON = 3,

		SHAPE_TYPE_COUNT,
	};

	class CShape
	{
	public:
		virtual bool IsContain(const CVector3& point) const = 0;
		virtual EShapeType shape_type() const = 0;
	};

	class CRect : public CShape
	{
	public:
		CRect();
		CRect(float xmin, float xmax, float zmin, float zmax);

		~CRect();

		bool IsContain(float x, float z) const;
		bool IsContain(const CVector3& point) const;
		EShapeType shape_type() const { return SHAPE_TYPE_RECT; }

		float xmin() const { return xmin_; }
		float xmax() const { return xmax_; }
		float zmin() const { return zmin_; }
		float zmax() const { return zmax_; }

		void set_xmin(float n) { xmin_ = n; }
		void set_xmax(float n) { xmax_ = n; }
		void set_zmin(float n) { zmin_ = n; }
		void set_zmax(float n) { zmax_ = n; }

	private:
		float xmin_;
		float xmax_;
		float zmin_;
		float zmax_;
	};

	class CCircle : public CShape
	{
	public:
		CCircle() : origin_point_(CVector3()), radius_(0.0f) { }
		CCircle(const CVector3& point, float radius) : origin_point_(point), radius_(radius) { }
		const CVector3& origin_point() const { return origin_point_; }
		float radius() const { return radius_; }

		virtual CVector3 GetPointAtArc(float angle) const;
		virtual bool IsContain(const CVector3& point) const;
		EShapeType shape_type() const { return SHAPE_TYPE_CIRCLE; }
	private:
		CVector3    origin_point_;
		float       radius_;
	};

	class CPolygon : public CShape
	{
	public:
		CPolygon(std::vector<CVector3>& points) { points_.assign(points.begin(), points.end()); }
		virtual bool IsContain(const CVector3& point) const;
		EShapeType shape_type() const { return SHAPE_TYPE_POLYGON; }

		const std::vector<CVector3>& points() const { return points_; }

	private:
		std::vector<CVector3>   points_;
	};

};//namespace common;


#endif

